#include <QtCore/QDebug>
#include <QyDebug.h>
#include "QyGLObject.h"
#include "3rdparty/glextensions.h"

using namespace QyGL;

/**************************************************************************************************/
qreal Texture::p_maxAnisotropy = 1.0;
/**************************************************************************************************/

void Texture::bind() {

    if ( isValid() ) {

        glBindTexture( p_target, p_textureId );
//         qDebug() << "bind" << p_textureId << p_target << GL_TEXTURE_2D;

    }

}

void Texture::unbind() {

    if ( isValid() )
        glBindTexture( p_target, 0 );

}

/*static */void Texture::setFilter( qint32 filter, qint32 target ) {

    if (!target) {

        setFilter( filter, GL_TEXTURE_1D );
        setFilter( filter, GL_TEXTURE_2D );
#if defined(GL_TEXTURE_3D)
        setFilter( filter, GL_TEXTURE_3D );
#endif

        return;
    }

    qint32 valueMin;
    qint32 valueMag;

    switch (filter) {

    default:
    case Linear:
        valueMin = GL_NEAREST;
        valueMag = GL_NEAREST;
        break;

    case Bilinear:
        valueMin = GL_LINEAR;
        valueMag = GL_LINEAR;
        break;

    case Trilinear:
        valueMin = GL_LINEAR_MIPMAP_LINEAR;
        valueMag = GL_LINEAR;
        break;

    case Anisotropic:
#if defined(GL_TEXTURE_MAX_ANISOTROPY_EXT)
        valueMin = GL_LINEAR_MIPMAP_LINEAR;
        valueMag = GL_LINEAR;
        glTexParameterf( target, GL_TEXTURE_MAX_ANISOTROPY_EXT, p_maxAnisotropy );
#else
        valueMin = GL_LINEAR;
        valueMag = GL_LINEAR;
#endif
        break;

    }

    glTexParameteri( target, GL_TEXTURE_MIN_FILTER, valueMin );
    glTexParameteri( target, GL_TEXTURE_MAG_FILTER, valueMag );

}

/**************************************************************************************************/

void Object::paint() {

    VertexVector::Iterator current = p_vertexes.begin(),
        end = p_vertexes.end();

    if ( p_paintFlags == All ) {

        bool textured = p_texture.isValid();

        if (textured)
            p_texture.bind();

        glBegin(p_type);
        for ( ; current != end; current ++ ) {

            glColor3dv( (*current).color.p );
            glTexCoord2dv( (*current).texCoord.p );
            glVertex3dv( (*current).pos.p );
            glNormal3dv( (*current).normal.p );

        }
        glEnd();

        if (textured)
            p_texture.unbind();

    } else if ( p_paintFlags == 0 ) {

        glBegin(p_type);
        for ( ; current != end; current ++ )
            glVertex3dv( (*current).pos.p );
        glEnd();

    } else {

        bool colored = ( p_paintFlags & Colors );
        bool useTexCoords = ( p_paintFlags & TexCoords );
        bool useNormal = ( p_paintFlags & Normals );
        bool textured = ( p_paintFlags & Textured ) && p_texture.isValid();

        // QyDbgLocation();
        // QyDbgValue(colored);
        // QyDbgValue(useTexCoords);
        // QyDbgValue(useNormal);
        // QyDbgValue(textured);

        if (textured) {

            // QyDbgValue( p_texture.textureId() );
            p_texture.bind();

        }

        glBegin(p_type);
        for ( ; current != end; current ++ ) {

            if (colored)
                glColor3dv( (*current).color.p );

            if (useTexCoords)
                glTexCoord2d( (*current).texCoord.p[0], (*current).texCoord.p[1] );

            if (useNormal)
                glNormal3dv( (*current).normal.p );

            glVertex3dv( (*current).pos.p );

        }
        glEnd();

        if (textured)
            p_texture.unbind();


    }

}

void Object::paintOn( const QyMath::Point & pos ) {

    glPushMatrix();{

        glTranslated( pos.p[0], pos.p[1], pos.p[2] );
        paint();

    }glPopMatrix();

}

/**************************************************************************************************/

void ObjectCache::paint() {

    if (!p_object)
        return;

    if ( p_object->p_modifiyCount != p_modifiyCount ) {

        p_modifiyCount = p_object->p_modifiyCount;

        if (p_cache)
            glDeleteLists( p_cache, 1 );

        p_cache = glGenLists(1);

        glNewList( p_cache, GL_COMPILE_AND_EXECUTE );
        p_object->paint();
        glEndList();

    } else {

        glCallList(p_cache);

    }

}

void ObjectCache::paintOn( const QyMath::Point & pos ) {

    glPushMatrix();{

        glTranslated( pos.p[0], pos.p[1], pos.p[2] );
        paint();

    }glPopMatrix();

}
